package comp;

import ast.*;
import java.io.PrintWriter;
import java.util.ArrayList;
import lexer.Lexer;
import lexer.Symbol;

public class Compiler {

    // compile must receive an input with an character less than
    // p_input.lenght
    public Program compile(char[] input, PrintWriter outError) {

        error = new CompilerError(new PrintWriter(outError));
        symbolTable = new SymbolTable();
        lexer = new Lexer(input, error);
        error.setLexer(lexer);


        Program program = null;
        try {
            lexer.nextToken();
            if (lexer.token == Symbol.EOF) {
                error.show("Unexpected end of file");
            }
            program = program();
            if (lexer.token != Symbol.EOF) {
                program = null;
                error.show("End of file expected");
            }
        } catch (Exception e) {
            // the below statement prints the stack of called methods.
            // of course, it should be removed if the compiler were
            // a production compiler.

            //e.printStackTrace();
            program = null;
        }

        return program;
    }

    private Program program() {
        // Program ::=  ClassDec { ClassDec }

        /**
         * FALTA MONTAR A LISTA DE classDec() PARA RETORNAR
         */
        
        ArrayList<ClassDec> classList = new ArrayList<>();
        
        classList.add(classDec());
        while (lexer.token == Symbol.CLASS) {
            classList.add(classDec());
        }
        return new Program(new ArrayList<ClassDec>());
    }

    private ClassDec classDec() {
        // Note que os metodos desta classe nao correspondem exatamente as regras
        // da gramatica. Este metodo classDec, por exemplo, implementa
        // a producao ClassDec (veja abaixo) e partes de outras producoes.

        /*
         * ClassDec ::= "class" Id [ "extends" Id ] "{" MemberList "}"
         * MemberList ::= { Member } 
         * Member ::= InstVarDec | MethodDec
         * InstVarDec ::= [ "static" ] "private" Type IdList ";" 
         * MethodDec ::= Qualifier ReturnType Id "("[ FormalParamDec ] ")" "{" StatementList "}" 
         * Qualifier ::= [ "static" ] ( "private" | "public" )
         */
        
        
        /**
         *  CRIAR UM OBJETO ClassDec PARA USAR DE RETORNO DA FUNCAO
         */
        
        
        // verifica se tem o "class"
        if (lexer.token != Symbol.CLASS) {
            error.show("'class' expected");
        }
        lexer.nextToken();
        
        //verifica se tem o Id
        if (lexer.token != Symbol.IDENT) {
            error.show(CompilerError.ident_expected);
        }
        String className = lexer.getStringValue(); // pega o nome do Id
        lexer.nextToken();
        
        // Cria um objeto ClassDec
        // Os outros atributos de ClassDec sao passados com Getters e Setters
        ClassDec cDec = new ClassDec(className);
        
        // verifica se tem 'extends'
        if (lexer.token == Symbol.EXTENDS) {
            lexer.nextToken();
            // verifica se tem Id
            if (lexer.token != Symbol.IDENT) {
                error.show(CompilerError.ident_expected);
            }
            String superclassName = lexer.getStringValue();
            // define a superclasse
            cDec.setSuperclass(new ClassDec(superclassName));
            lexer.nextToken();
        }
        
        // verifica se tem '{'
        if (lexer.token != Symbol.LEFTCURBRACKET) {
            error.show("{ expected", true);
        }
        lexer.nextToken();

        /**
         * ACHO QUE FALTA UM IF PARA VER O [ 'static' ]
         * E DEPOIS IR PARA O PROXIMO TOKEN
         */
        
        if (lexer.token != Symbol.STATIC) {
            error.show("'static' expected", true);
        }
        lexer.nextToken();
        
        
        /**
         * QUANDO FIZER ESTE WHILE, NO FINAL, TEM QUE MONTAR UMA LISTA DE Member
         * QUE É UMA LISTA DE DELCARACOES DE CLASSES E/OU METODOS
         */
        
        
        // Cria uma lista para os metodos e outra para as variaveis
        // Nao sei o que fazer com o 'static' 
        MethodList mList = new MethodList();
        InstanceVariableList vList = new InstanceVariableList();
        
        while (lexer.token == Symbol.PRIVATE
                || lexer.token == Symbol.PUBLIC) {

            Symbol qualifier;
            switch (lexer.token) {
                case PRIVATE:
                    lexer.nextToken();
                    qualifier = Symbol.PRIVATE;
                    break;
                case PUBLIC:
                    lexer.nextToken();
                    qualifier = Symbol.PUBLIC;
                    break;
                default:
                    error.show("private, or public expected");
                    qualifier = Symbol.PUBLIC;
            }
            Type t = type();
            if (lexer.token != Symbol.IDENT) {
                error.show("Identifier expected");
            }
            String name = lexer.getStringValue();
            lexer.nextToken();
            
            // aqui ele verifica se é um MethodDec ou InstVarDec
            if (lexer.token == Symbol.LEFTPAR) {
                mList.addElement(methodDec(t, name, qualifier));
            } else if (qualifier != Symbol.PRIVATE) { // verificacao semantica                
                error.show("Attempt to declare a public instance variable");
            } else {
                vList.addElement(instanceVarDec(t, name, qualifier));
            }
        }
        
        
        if (lexer.token != Symbol.RIGHTCURBRACKET) {
            error.show("public/private or \"}\" expected");
        }
        lexer.nextToken();
        
        return cDec;

    }

    ///// PAREI AQUI !!! ///////////////////////////////////////////////////
    
    private InstanceVariableList instanceVarDec(Type type, String name, Symbol qualifier) {
        //   InstVarDec ::= [ "static"  ] "private"  Type  IdList  ";"

        /**
         * só entra nessa funcao depois de ter a primeira parte da regra, falta
         * apenas MONTAR UMA LISTA DE Ids PARA E CONSTRUIR UM OBJETO
         * InstanceVariableList PARA USAR DE RETORNO DA FUNCAO
         */
        while (lexer.token == Symbol.COMMA) {
            lexer.nextToken();
            if (lexer.token != Symbol.IDENT) {
                error.show("Identifier expected");
            }
            String variableName = lexer.getStringValue();
            lexer.nextToken();
        }
        if (lexer.token != Symbol.SEMICOLON) {
            error.show(CompilerError.semicolon_expected);
        }
        lexer.nextToken();
    }

    private void methodDec(Type type, String name, Symbol qualifier) {
        /*
         * MethodDec ::= Qualifier ReturnType Id "("[ FormalParamDec ] ")" "{" StatementList "}"
         */

        /**
         * só entra nesse metodo depois de ter feito a primeira parte da regra,
         * entao só falta VERIFICAR E CRIAR UMA LISTA DE Parameters (QUE SERÁ
         * RETORNADA POR formalParamDec()), E CRIAR UMA LISTA DE STATEMENTS (QUE
         * SERÁ RETORNADA POR statementList())
         */
        lexer.nextToken();
        if (lexer.token != Symbol.RIGHTPAR) {
            // retorna uma lista de Parameter
            formalParamDec();
        }
        if (lexer.token != Symbol.RIGHTPAR) {
            error.show(") expected");
        }

        lexer.nextToken();
        if (lexer.token != Symbol.LEFTCURBRACKET) {
            error.show("{ expected");
        }

        lexer.nextToken();
        // retorna uma lista de Statements
        statementList();
        if (lexer.token != Symbol.RIGHTCURBRACKET) {
            error.show("} expected");
        }

        lexer.nextToken();

    }

    private void localDec(Type type) {
        // LocalDec ::= Type IdList ";"

        /**
         * só entra aqui depois de ter verificado o Type
         * FALTA APENAS MONTAR UMA LISTA DE Variable E RETORNAR NO MÉTODO
         * acho que não precisa verificar o ponto-e-virgula, ele faz isso em 
         * outro método
         */
        if (lexer.token != Symbol.IDENT) {
            error.show("Identifier expected");
        }
        Variable v = new Variable(lexer.getStringValue(), type);
        lexer.nextToken();
        while (lexer.token == Symbol.COMMA) {
            lexer.nextToken();
            if (lexer.token != Symbol.IDENT) {
                error.show("Identifier expected");
            }
            v = new Variable(lexer.getStringValue(), type);
            lexer.nextToken();
        }
    }

    private void formalParamDec() {
        //  FormalParamDec ::= ParamDec { "," ParamDec }

        /**
         * CRIAR UMA LISTA DE Parameter E RETORNAR (isto é, um objeto ParamList)
         */
        paramDec();
        while (lexer.token == Symbol.COMMA) {
            lexer.nextToken();
            paramDec();
        }
    }

    private void paramDec() {
        // ParamDec ::= Type Id

        /**
         * CRIA E RETORNA UM OBJETO Parameter
         */
        
        type();
        if (lexer.token != Symbol.IDENT) {
            error.show("Identifier expected");
        }
        lexer.nextToken();
    }

    private Type type() {
        // Type ::= BasicType | Id
        Type result;

        /**
         * VERIFICAR A OBSERVACAO DO ZÉ QUE ESTÁ EM BAIXO -- #corrija
         * PELO QUE EU ENTENDI, É QUANDO O TIPO SE REFERE A UMA CLASSE, 
         * OU SEJA É A DECLARACAO DE UM OBJETO
         */
        
        switch (lexer.token) {
            case VOID:
                result = Type.voidType;
                break;
            case INT:
                result = Type.intType;
                break;
            case BOOLEAN:
                result = Type.booleanType;
                break;
            case STRING:
                result = Type.stringType;
                break;
            case IDENT:
                //# corrija: faca uma busca na TS para buscar a classe
                // IDENT deve ser uma classe.
                result = null;
                break;
            default:
                error.show("Type expected");
                result = Type.undefinedType;
        }
        lexer.nextToken();
        return result;
    }

    private void compositeStatement() {
        // CompStatement ::= "{" { Statement } "}"
        
        /**
         * apenas chama o metodo statementList que retorna um objeto
         * StatementList e verifica se tem as chaves "{ }"
         */
        lexer.nextToken();
        statementList();
        if (lexer.token != Symbol.RIGHTCURBRACKET) {
            error.show("} expected");
        } else {
            lexer.nextToken();
        }
    }

    private void statementList() {
        // CompStatement ::= "{" { Statement } "}"
        
        /**
         *  CRIA E RETORNA UMA LISTA DE STATEMENTS (StatementList)
         */
        
        Symbol tk;
        // statements always begin with an identifier, if, read, write, ...
        while ((tk = lexer.token) != Symbol.RIGHTCURBRACKET
                && tk != Symbol.ELSE) {
            statement();
        }
    }

    private void statement() {
        /*
         * Statement ::= Assignment ";" | IfStat | WhileStat | MessageSend ";" 
         *              | ReturnStat ";" | ReadStat ";" | WriteStat ";" 
         *              | "break" ";" | ";" | CompStatement | LocalDec
         */
        
        /**
         * COMPLETAR OS CASES QUE FALTAM PARA A ANALISE SINTATICA (THIS E IDENT)
         * CRIAR OBJETOS PARA RETORNAR (O TIPO DE RETORNO SERÁ Statement E TODAS 
         * AS OBJETOS RETORNADOS SÃO HERDADOS DE Statement)
         */

        switch (lexer.token) {
            case THIS:
            case IDENT:
            case SUPER:
                assignmentMessageSendLocalVarDecStatement();
                break;
            case INT:
                lexer.nextToken();
                localDec(Type.intType);
                break;
            case BOOLEAN:
                lexer.nextToken();
                localDec(Type.booleanType);
                break;
            case STRING:
                lexer.nextToken();
                localDec(Type.stringType);
                break;
            case RETURN:
                returnStatement();
                break;
            case READ:
                readStatement();
                break;
            case WRITE:
                writeStatement();
                break;
            case IF:
                ifStatement();
                break;
            case BREAK:
                breakStatement();
                break;
            case WHILE:
                whileStatement();
                break;
            case SEMICOLON:
                nullStatement();
                break;
            case LEFTCURBRACKET:
                compositeStatement();
                break;
            default:
                error.show("Statement expected");
        }
    }

    private ExprList getRealParameters() {
        ExprList anExprList = null;
        
        /**
         * monta uma lista de expressoes (ExprList) entre parenteses
         * Acho que esta pronta!
         */

        if (lexer.token != Symbol.LEFTPAR) {
            error.show("( expected");
        }
        lexer.nextToken();
        if (startExpr(lexer.token)) {
            anExprList = exprList();
        }
        if (lexer.token != Symbol.RIGHTPAR) {
            error.show(") expected");
        }
        lexer.nextToken();
        return anExprList;
    }

    private void whileStatement() {
        // WhileStat := "while" "(" Expression ")" Statement
        
        /**
         * CRIAR E RETORNAR UM OBJETO WhileStatement
         */
        
        lexer.nextToken();
        if (lexer.token != Symbol.LEFTPAR) {
            error.show("( expected");
        }
        lexer.nextToken();
        expr();
        if (lexer.token != Symbol.RIGHTPAR) {
            error.show(") expected");
        }
        lexer.nextToken();
        statement();
    }

    private void ifStatement() {
        // IfStat := "if" "(" Expression ")" Statement [ "else" Statement ]
        
        /**
         * CRIAR E RETORNAR UM OBJETO IfStatement
         */
        
        lexer.nextToken();
        if (lexer.token != Symbol.LEFTPAR) {
            error.show("( expected");
        }
        lexer.nextToken();
        expr();
        if (lexer.token != Symbol.RIGHTPAR) {
            error.show(") expected");
        }
        lexer.nextToken();
        statement();
        if (lexer.token == Symbol.ELSE) {
            lexer.nextToken();
            statement();
        }
    }

    private void returnStatement() {
        // ReturnStat := "return" Expression
        
        /**
         * CRIAR E RETORNAR UM OBJETO ReturnStatement
         * (a classe nao foi criada, mas precisa criar por causa do genKrakatoa)
         */

        lexer.nextToken();
        expr();
        if (lexer.token != Symbol.SEMICOLON) {
            error.show(CompilerError.semicolon_expected);
        }
        lexer.nextToken();
    }

    private void readStatement() {
        // ReadStat := "read" "(" LeftValue { "," LeftValue } ")"
        // LeftValue := ["this" "." Id] | Id
        
        /**
         * CRIA E RETORNA UM OBJETO ReadStatement
         */
        
        lexer.nextToken();
        if (lexer.token != Symbol.LEFTPAR) {
            error.show("( expected");
        }
        lexer.nextToken();
        
        // Monta a lista de LeftValue (que é uma lista de IDs e logo uma InstanceVariableList ?? - CONFIRMAR)
        while (true) {
            if (lexer.token == Symbol.THIS) {
                lexer.nextToken();
                if (lexer.token != Symbol.DOT) {
                    error.show(". expected");
                }
                lexer.nextToken();
            }
            if (lexer.token != Symbol.IDENT) {
                error.show(CompilerError.ident_expected);
            }

            String name = (String) lexer.getStringValue();
            lexer.nextToken();
            if (lexer.token == Symbol.COMMA) {
                lexer.nextToken();
            } else {
                break;
            }
        }

        if (lexer.token != Symbol.RIGHTPAR) {
            error.show(") expected");
        }
        lexer.nextToken();
        if (lexer.token != Symbol.SEMICOLON) {
            error.show(CompilerError.semicolon_expected);
        }
        lexer.nextToken();
    }

    private void writeStatement() {
        // WriteStat := "write" "(" ExpressionList ")" 
        
        /**
         * CRIA E RETORNA UM OBJETO WriteStatement
         */

        lexer.nextToken();
        if (lexer.token != Symbol.LEFTPAR) {
            error.show("( expected");
        }
        lexer.nextToken();
        exprList();
        if (lexer.token != Symbol.RIGHTPAR) {
            error.show(") expected");
        }
        lexer.nextToken();
        if (lexer.token != Symbol.SEMICOLON) {
            error.show(CompilerError.semicolon_expected);
        }
        lexer.nextToken();
    }

    private void breakStatement() {
        /**
         * Na gramatica nao existe um breakStat, mas acho que precisa criar uma
         * classe por causa do genKrakatoa 
         * Logo, precisa CRIAR E RETORNAR UM OBJETO BreakStatement
         */
        lexer.nextToken();
        if (lexer.token != Symbol.SEMICOLON) {
            error.show(CompilerError.semicolon_expected);
        }
        lexer.nextToken();
    }

    private void nullStatement() {
        /**
         * ACHO QUE PRECISA CRIAR UM OBJETO NullStatement E RETORNAR (por causa
         * do genKrakatoa)
         */
        
        lexer.nextToken();
    }

    private ExprList exprList() {
        // ExpressionList ::= Expression { "," Expression }

        /**
         * ESTA PRONTO !?
         */

        ExprList anExprList = new ExprList();
        anExprList.addElement(expr());
        while (lexer.token == Symbol.COMMA) {
            lexer.nextToken();
            anExprList.addElement(expr());
        }
        return anExprList;
    }

    private Expr expr() {
        // Expression := SimpleExpression [Relation SimpleExpression]
        
        /**
         * ESTA PRONTO!?
         */
        
        Expr left = simpleExpr();
        Symbol op = lexer.token;
        if (op == Symbol.EQ || op == Symbol.NEQ
                || op == Symbol.LE || op == Symbol.LT
                || op == Symbol.GE || op == Symbol.GT) {
            lexer.nextToken();
            Expr right = simpleExpr();
            left = new CompositeExpr(left, op, right);
        }
        return left;
    }

    private Expr simpleExpr() {
         // SimpleExpression := Term {LowOperator Term}
        
        /**
         * ESTA PRONTO!?
         */
        
        Symbol op;

        Expr left = term();
        while ((op = lexer.token) == Symbol.MINUS || op == Symbol.PLUS
                || op == Symbol.OR) {
            lexer.nextToken();
            Expr right = term();
            left = new CompositeExpr(left, op, right);
        }
        return left;
    }

    private Expr term() {
         // Term := SignalFactor {HighOperator SignalFactor}
        
        /**
         * ESTA PRONTO!?
         */
        
        Symbol op;

        Expr left = signalFactor();
        while ((op = lexer.token) == Symbol.DIV || op == Symbol.MULT
                || op == Symbol.AND) {
            lexer.nextToken();
            Expr right = signalFactor();
            left = new CompositeExpr(left, op, right);
        }
        return left;
    }

    private Expr signalFactor() {
        // SignalFactor := [Signal] Factor
        // Signal := "+" | "-"
        
        /**
         * PRONTO!?
         */
        
        Symbol op;
        if ((op = lexer.token) == Symbol.PLUS || op == Symbol.MINUS) {
            lexer.nextToken();
            return new SignalExpr(op, factor());
        } else {
            return factor();
        }
    }

    private Expr factor() {
        /*
         * Factor ::= BasicValue | RightValue | MessageSend | "(" Expression ")"
         *             | "!" Factor | "null" | ObjectCreation 
         * BasicValue ::= IntValue | BooleanValue | StringValue 
         * BooleanValue ::= "true" | "false"
         * RightValue ::= "this" [ "." Id ] | Id 
         * MessageSend ::= ReceiverMessage "." Id "(" [ ExpressionList ] ")" 
         * ReceiverMessage ::= "super" | Id | "this" | "this" "." Id 
         * ObjectCreation ::= "new" Id "(" ")"
         */
        
        
        /**
         * FALTA FAZER AS OBSERVACOES QUE O ZÈ DEIXOU 
         */

        Expr e;
        Variable aVariable;
        ClassDec aClass;
        //MethodDec aMethod;
        InstanceVariable anInstanceVariable;

        switch (lexer.token) {
            case NOT:
                lexer.nextToken();
                e = expr();
                return new UnaryExpr(e, Symbol.NOT);
            case LITERALSTRING:
                String literalString = lexer.getLiteralStringValue();
                lexer.nextToken();
                return new LiteralStringExpr(literalString);
            case TRUE:
                lexer.nextToken();
                return BooleanExpr.True;
            case FALSE:
                lexer.nextToken();
                return BooleanExpr.False;
            case LEFTPAR:
                lexer.nextToken();
                e = expr();
                if (lexer.token != Symbol.RIGHTPAR) {
                    error.show(") expected");
                }
                lexer.nextToken();
                return new ParenthesisExpr(e);
            case NULL:
                lexer.nextToken();
                return new NullExpr();
            case NUMBER:
                return number();
            case NEW:
                lexer.nextToken();
                if (lexer.token != Symbol.IDENT) {
                    error.show("Identifier expected");
                }

                String className = lexer.getStringValue();
                
                // encontre a classe className in symbol table 
                // ClassDec aClass = symbolTable.getInGlobal(className); 
                // if ( aClass ==null ) ...


                lexer.nextToken();
                if (lexer.token != Symbol.LEFTPAR) {
                    error.show("( expected");
                }
                lexer.nextToken();
                if (lexer.token != Symbol.RIGHTPAR) {
                    error.show(") expected");
                }
                lexer.nextToken();
                /*
                 * return an object representing the creation of an object
                 * something as return new Cria_um_objeto(aClass); é importante
                 * nao utilizar className, uma string e sim aClass, um objeto.
                 */
                return null;
            default:
                String variableName,
                 methodName;
                ExprList exprList;
                /*
                 * there are seven cases to consider : super.m() this.x this
                 * this.m() this.x.m(); x x.m()
                 *
                 * in which x is either a variable or an instance variable and m
                 * is a method
                 */
                switch (lexer.token) {
                    case SUPER:
                        // expression of the kind "super.m()"
                        lexer.nextToken();
                        if (lexer.token != Symbol.DOT) {
                            error.show(". expected");
                        }
                        lexer.nextToken();
                        if (lexer.token != Symbol.IDENT) {
                            error.show(CompilerError.ident_expected);
                        }
                        methodName = lexer.getStringValue();
                        lexer.nextToken();
                        exprList = getRealParameters();
                        //#  corrija
                  /*
                         * deve existir uma vari�vel de inst�ncia currentClass.
                         * aClass = currentClass.getSuperclass(); if ( aClass ==
                         * null ) ... aMethod = aClass.getMethod(methodName); if
                         * ( aMethod == null ) ...
                         *
                         * return new MessageSendToSuper( aClass, aMethod,
                         * exprList);
                         */
                        break;
                    case THIS:
                        lexer.nextToken();
                        if (lexer.token != Symbol.DOT) {
                            // expression of the kind "this"
                            //# corrija
                     /*
                             * Verifique se n�o estamos em um m�todo est�tico o
                             * construtor da classe ThisExpr deve tomar a classe
                             * corrente como par�metro. Por qu� ? return new
                             * ThisExpr(currentClass);
                             */
                        } else {
                            lexer.nextToken();
                            if (lexer.token != Symbol.IDENT) {
                                error.show(CompilerError.ident_expected);
                            }
                            // it may be method name or an instance variable
                            String ident = lexer.getStringValue();
                            lexer.nextToken();
                            switch (lexer.token) {
                                case LEFTPAR:
                                    // expression of the kind "this.m()"
                                    //# corrija
                                    exprList = getRealParameters();
                                    /*
                                     * procure o m�todo ident na classe
                                     * corrente: aMethod =
                                     * currentClass.searchMethod(ident); if (
                                     * aMethod == null ) ... confira se aMethod
                                     * pode aceitar os par�metros de exprList.
                                     * return new MessageSendToSelf( aMethod,
                                     * exprList );
                                     */
                                    break;
                                case DOT:
                                    // expression of the kind "this.x.m()"
                                    //# corrija
                                    lexer.nextToken();
                                    if (lexer.token != Symbol.IDENT) {
                                        error.show(CompilerError.ident_expected);
                                    }
                                    methodName = lexer.getStringValue();
                                    lexer.nextToken();
                                    exprList = getRealParameters();
                                /*
                                 * em this.x.m(), x est� em ident e m em
                                 * methodName procure por x na lista de
                                 * vari�veis de inst�ncia da classe corrente:
                                 * anInstanceVariable =
                                 * currentClass.searchInstanceVariable(ident);
                                 * if ( anInstanceVariable == null ) ... pegue a
                                 * classe declarada de x, o tipo de x: if ( !
                                 * (anInstanceVariable.getType() instanceof
                                 * ClassDec) ) ... // tipo de x n�o � uma
                                 * classe, erro confira se a classe de x possui
                                 * m�todo m: aClass = (ClassDec )
                                 * anInstanceVariable.getType(); aMethod =
                                 * aClass.searchMethod(methodName); if ( aMethod
                                 * == null ) ...
                                 *
                                 * return new MessageSendToVariable(
                                 * anInstanceVariable, aMethod, exprList );
                                 *
                                 */
                                default:
                                // expression of the kind "this.x"
                                //# corrija
                           /*
                                 * procure x na lista de vari�veis de inst�ncia
                                 * da classe corrente: anInstanceVariable =
                                 * currentClass.searchInstanceVariable(ident);
                                 * if ( anInstanceVariable == null ) ... return
                                 * new VariableExpr( anInstanceVariable )
                                 */
                            }

                        }
                        break;
                    case IDENT:
                        variableName = lexer.getStringValue();
                        lexer.nextToken();
                        if (lexer.token != Symbol.DOT) {
                            // expression of the kind "x"
                            //# corrija
                     /*
                             * if ( (aVariable =
                             * symbolTable.get...(variableName)) == null ) ...
                             * return new VariableExpr(aVariable);
                             */
                        } else {
                            // expression of the kind "x.m()"
                            lexer.nextToken();  // eat the dot
                            switch (lexer.token) {
                                case IDENT:
                                    methodName = lexer.getStringValue();
                                    lexer.nextToken();
                                    exprList = getRealParameters();
                                    //#  corrija
                           /*
                                     *
                                     * if ( (aVariable =
                                     * symbolTable.getInLocal(variableName)) !=
                                     * null ) { // x is a variable Type t =
                                     * aVariable.getType(); teste se t � do tipo
                                     * ClassDec nesta linha aClass = (ClassDec )
                                     * t; verifique se a classe aClass possui um
                                     * m�todo chamado methodName que pode
                                     * receber como par�metros as express�es de
                                     * exprList. Algo como (apenas o in�cio):
                                     * aMethod =
                                     * aClass.searchMethod(methodName); ...
                                     * return new MessageSendToVariable(
                                     * aVariable, aMethod, exprList);
                                     *
                                     * }
                                     * else { // em "x.m()", x is not a
                                     * variable. Should be a class name if (
                                     * (aClass =
                                     * symbolTable.getInGlobal(variableName)) ==
                                     * null ) ... nesta linha, verifique se
                                     * methodName � um m�todo est�tico da classe
                                     * aClass que pode aceitar como par�metros
                                     * as express�es de exprList. Algo como
                                     * (apenas o in�cio): aStaticMethod =
                                     * aClass.searchStaticMethod(methodName);
                                     * ... return new MessageSendStatic(aClass,
                                     * aStaticMethod, exprList); }
                                     *
                                     *
                                     */

                                    break;
                                default:
                                    error.show(CompilerError.ident_expected);
                            }
                        }
                        break;
                    default:
                        error.show(CompilerError.ident_expected);
                }
                return null;
        }

    }

    private NumberExpr number() {

        NumberExpr e = null;

        // the number value is stored in lexer.getToken().value as an object of Integer.
        // Method intValue returns that value as an value of type int.
        int value = lexer.getNumberValue();
        lexer.nextToken();
        return new NumberExpr(value);
    }

    private Statement assignmentMessageSendLocalVarDecStatement() {
        /*
         * Assignment ::= LeftValue "=" Expression LeftValue} ::= [ "this" "." ]
         * Id MessageSend ::= ReceiverMessage "." Id "(" [ ExpressionList ] ")"
         * ReceiverMessage ::= "super" | Id | "this" | "this" "." Id LocalDec
         * ::= Type IdList ";"
         */


        // an assignment, a message send or a local variable declaration
	      /*
         * ##########################################################################
         * ##########################################################################
         * IMPORTANTE: a implementa��o deste m�todo � muit�ssimo parecido com o
         * do m�todo factor. Neste m�todo, factor, coloquei **muito** mais
         * partes implementadas do que neste m�todo
         * assignmentMessageSendLocalVarDecStatement. A grande diferen�a entre
         * os dois m�todos � que factor analisa uma express�o e
         * assignmentMessageSendLocalVarDecStatement analisa uma instru��o. Isto
         * �, um envio de mensagem "x.m()" em factor deve retornar um valor e em
         * assignmentMessageSendLocalVarDecStatement n�o deve retornar nada.
         * Resumindo: fa�a factor primeiro e depois copie e cole grande parte do
         * que voc� fez para este m�todo.
         *
         * ##########################################################################
         * ##########################################################################
         *
         */


        String methodName, variableName;
        ExprList exprList;
        Statement result = null;
        /*
         * there are eight possibilities: this.id() this.id = expr this.id.id()
         * super.id() id = expr id.id()
         *
         * Id IdList // Id is a type
         */

        switch (lexer.token) {
            case THIS:
                lexer.nextToken();
                if (lexer.token != Symbol.DOT) {
                    error.show(". expected");
                }
                lexer.nextToken();
                if (lexer.token != Symbol.IDENT) {
                    error.show(CompilerError.ident_expected);
                }
                String ident = lexer.getStringValue();
                lexer.nextToken();
                switch (lexer.token) {
                    case ASSIGN:
                        // this.id = expr
                        lexer.nextToken();
                        Expr anExpr = expr();
                        //# corrija
	                  /*
                         * result = new AssignmentStatement( pointer to instance
                         * variable, anExpr);
                         */
                        break;
                    case DOT:
                        // this.id.id()
                        lexer.nextToken();
                        if (lexer.token != Symbol.IDENT) {
                            error.show(CompilerError.ident_expected);
                        }
                        methodName = lexer.getStringValue();
                        lexer.nextToken();
                        exprList = getRealParameters();
                        //# corrija
	                  /*
                         * result = new MessageSendStatement( new
                         * MessageSendToVariable( pointer to variable, pointer
                         * to method, exprList) );
                         */
                        break;
                    case LEFTPAR:
                        // this.id()
                        exprList = getRealParameters();
                        //# corrija
	                  /*
                         * result = new MessageSendStatement( new
                         * MessageSendToSelf( pointer to method, exprList ) );
                         */
                        break;
                    default:
                        error.show(CompilerError.ident_expected);
                }
                break;
            case SUPER:
                // super.id()
                lexer.nextToken();
                if (lexer.token != Symbol.DOT) {
                    error.show(". expected");
                }
                lexer.nextToken();
                if (lexer.token != Symbol.IDENT) {
                    error.show(CompilerError.ident_expected);
                }
                methodName = lexer.getStringValue();
                lexer.nextToken();
                exprList = getRealParameters();
                //# corrija
                // result = new MessageSendStatement(
                //     new MessageSendToSuper( pointer to class, pointer to method, exprList) );
                break;
            case IDENT:
                variableName = lexer.getStringValue();
                lexer.nextToken();
                switch (lexer.token) {
                    case ASSIGN:
                        // id = expr
                        lexer.nextToken();
                        Expr anExpr = expr();
                        //# corrija
	                  /*
                         * result = new AssignmentStatement( pointer to
                         * variable, anExpr );
                         */
                        break;
                    case IDENT:
                        // id id;
                        // variableName id

                        // variableName must be the name of a class
                        // replace null in the statement below by
                        // a point to the class named variableName.
                        // A search in the symbol table is necessary.
                        localDec(null);
                        break;
                    case DOT:
                        // id.id()
                        lexer.nextToken();
                        methodName = lexer.getStringValue();
                        lexer.nextToken();
                        exprList = getRealParameters();
                        //# corrija
	                  /*
                         * result = new MessageSendStatement( new
                         * MessageSendToVariable( pointer to variable, pointer
                         * to method, exprList ) );
                         */
                        break;
                    default:
                        error.show(". or = expected");
                }
                break;
            default:
                error.show("'this', 'super', basic type or identifier expected");
        }
        if (lexer.token != Symbol.SEMICOLON) {
            error.show(CompilerError.semicolon_expected);
        }
        lexer.nextToken();
        return result;
    }

    private boolean startExpr(Symbol token) {

        return lexer.token == Symbol.FALSE
                || lexer.token == Symbol.TRUE
                || lexer.token == Symbol.NOT
                || lexer.token == Symbol.THIS
                || lexer.token == Symbol.NUMBER
                || lexer.token == Symbol.SUPER
                || lexer.token == Symbol.LEFTPAR
                || lexer.token == Symbol.NULL
                || lexer.token == Symbol.IDENT
                || lexer.token == Symbol.LITERALSTRING;

    }
    private SymbolTable symbolTable;
    private Lexer lexer;
    private CompilerError error;
}
